using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SqlServer.Dts.Runtime;
using Microsoft.SharedSource.SqlServer.MDDE.Repository;
using Microsoft.SharedSource.SqlServer.MDDE.Components;
using System.Xml;

namespace Microsoft.SharedSource.SqlServer.MDDE.Middleware
{
    public static partial class MDDEManager
    {
        public static OperationResult ImportPackage(
            string repositoryConnectionString, string templateName,
            string packageBody, string packageName)
        {
            OperationResult result = OperationResult.Empty;

            // Create the package execution DAG.
            Package package = new Package();
            package.LoadFromXML(packageBody, null);
            package.Name = packageName;
            MDDEExecutionDAG packageDag = new MDDEExecutionDAG(package);


            // Get the template.
            MDDERepository repository = new MDDERepository(repositoryConnectionString);

            if (!repository.PackageTemplates.ContainsTemplate(templateName))
            {
                result.OperationLog.Add("Template - " + templateName +
                    " doesn't exists in the repository");
                result.SuccessStatus = false;
                return result;
            }

            MDDEPackageTemplate mddeTemplate = repository.PackageTemplates[templateName];

            // Create the template execution DAG.
            Package template = new Package();
            template.LoadFromXML(mddeTemplate.Body, null);
            template.Name = packageName;
            MDDEExecutionDAG templateDag = new MDDEExecutionDAG(template);

            // Compare template's structure with package's structure
            if (!templateDag.CompareTo(packageDag, true))
            {
                result.OperationLog.Add("Package - " + packageName +
                    " is structurly different from template - " + templateName);
                result.SuccessStatus = false;
                return result;
            }

            XmlDocument changeXML = packageDag.GetChangeXml(templateDag);

            //Varify that template can be fixed with generated changeXml.
            if (!templateDag.ApplyChanges(changeXML, packageName))
            {
                result.OperationLog.Add("Package - " + packageName +
                    " can not be validated.");
                result.SuccessStatus = false;
                return result;
            }

            //Create and save new MDDE package.
            MDDEPackage mddePackage = new MDDEPackage(mddeTemplate, packageName);

            string generatedPackageBody;
            (templateDag.InnerComponent as Package).SaveToXML(out generatedPackageBody, null);

            mddePackage.Body = generatedPackageBody;
            mddePackage.Version = "1.0.0.0";
            mddePackage.ChangesFromTemplate = changeXML;
            mddePackage.Create();

            result.SuccessStatus = true;
            return result;
        }

        public static OperationResult UpdatePackage(
            string repositoryConnectionString, string templateName, string packageName,
            string packageVersion, XmlDocument changeXML)
        {
            OperationResult results = new OperationResult();
            results = OperationResult.Empty;

            try
            {
                MDDERepository repository = new MDDERepository(repositoryConnectionString);

                if (!repository.PackageTemplates.ContainsTemplate(templateName))
                {
                    results.SuccessStatus = false;
                    results.OperationLog.Add(
                        "Template " + templateName + " doesn't exists in given repository");
                    return results;
                }
                MDDEPackageTemplate mddeTemplate = repository.PackageTemplates[templateName];

                if (!mddeTemplate.Packages.ContainsPackage(packageName))
                {
                    results.OperationLog.Add("Package \"" + packageName + "\" doesn't exists in the repository");
                    results.SuccessStatus = false;
                    return results;
                }

                MDDEPackage mddePackage = mddeTemplate.Packages[packageName];

                Package package = new Package();
                package.LoadFromXML(mddePackage.Body, null);
                MDDEExecutionDAG packageDag = new MDDEExecutionDAG(package);

                if (!packageDag.ApplyChanges(changeXML))
                {
                    results.OperationLog.Add(
                        "Not able to apply given changes on package - " + package.Name);
                    results.SuccessStatus = false;
                    return results;
                }

                Package template = new Package();
                template.LoadFromXML(mddePackage.ParentTemplate.Body, null);
                MDDEExecutionDAG templateDag = new MDDEExecutionDAG(template);

                XmlDocument newChangeXml = packageDag.GetChangeXml(templateDag);

                string newPackageBody;
                (packageDag.InnerComponent as Package).SaveToXML(out newPackageBody, null);
                mddePackage.Body = newPackageBody;
                mddePackage.ChangesFromTemplate = newChangeXml;
                mddePackage.Update();
                results.SuccessStatus = true;
            }
            catch (Exception ex)
            {
                results.OperationLog.Add(ex.Message);
                results.SuccessStatus = false;
            }
            return results;
        }

        public static OperationResult DeletePackage(string repositoryConnectionString,
            string templateName, string packageName, string packageVersion)
        {
            OperationResult result = OperationResult.Empty;

            // Delete the package.
            try
            {
                MDDERepository repository = new MDDERepository(repositoryConnectionString);

                if (!repository.PackageTemplates.ContainsTemplate(templateName))
                {
                    result.SuccessStatus = false;
                    result.OperationLog.Add(
                        "Template " + templateName + " doesn't exists in given repository");
                    return result;
                }

                MDDEPackageTemplate mddeTemplate = repository.PackageTemplates[templateName];

                if (!mddeTemplate.Packages.ContainsPackage(packageName))
                {
                    result.OperationLog.Add("Package \"" + packageName +
                        "\" doesn't exists in the repository");

                    return result;
                }

                MDDEPackage package = mddeTemplate.Packages[packageName];

                package.Delete();

                result.SuccessStatus = true;
            }
            catch (Exception ex)
            {
                result.OperationLog.Add(ex.Message);
            }
            return result;
        }

        public static OperationResult ExecutePackage(string repositoryConnectionString,
            string templateName, string packageName, string packageVersion)
        {
            OperationResult result = OperationResult.Empty;
            
            try
            {
                MDDERepository repository = new MDDERepository(repositoryConnectionString);

                if (!repository.PackageTemplates.ContainsTemplate(templateName))
                {
                    result.SuccessStatus = false;
                    result.OperationLog.Add(
                        "Template " + templateName + " doesn't exists in given repository");
                    return result;
                }

                MDDEPackageTemplate mddeTemplate = repository.PackageTemplates[templateName];

                if (!mddeTemplate.Packages.ContainsPackage(packageName))
                {
                    result.OperationLog.Add("Package \"" + packageName +
                        "\" doesn't exists in the repository");

                    return result;
                }

                MDDEPackage mddePackage = mddeTemplate.Packages[packageName];

                Package package = new Package();
                package.LoadFromXML(mddePackage.Body, null);
                DTSExecResult executionResult = package.Execute(package.Connections, package.Variables, null, null, null);

                if (executionResult == DTSExecResult.Success)
                {
                    result.SuccessStatus = true;                    
                }
                else
                {
                    result.SuccessStatus = false;
                    for (int errorIndex = 0; errorIndex < package.Errors.Count; errorIndex++)
                    {
                        result.OperationOutputParameters.Add("Error Code: "
                            + package.Errors[errorIndex].ErrorCode + "Description: " 
                            + package.Errors[errorIndex].Description);
                    }
                }
            }
            catch (Exception ex)
            {
                result.OperationLog.Add(ex.Message);
            }

            return result;
        }

        // Sandeep: Needed this method.
        public static XmlDocument GetPackageDetails(string repositoryConnectionString, string templateName, string packageName)
        {
            MDDERepository repository =
                new MDDERepository(repositoryConnectionString);

            MDDEPackage package = repository.PackageTemplates[templateName].Packages[packageName];

            // Create the details document.
            XmlDocument packageDetails = new XmlDocument();

            // Add the root element.
            XmlElement rootElement = packageDetails.CreateElement("MDDEPackage");
            packageDetails.AppendChild(rootElement);

            AppendAttribute(rootElement, new string[] { 
                "Name", package.Name, 
                "TemplateName", package.ParentTemplate.Name });

            XmlElement xChangeFromTemplate =
                AppendChildElement(rootElement, "ChangesFromTemplate", null);

            xChangeFromTemplate.InnerXml =
                package.ChangesFromTemplate.OuterXml;

            return packageDetails;
        }

        //public static OperationResult ComparePackages(
        //    XmlDocument package1, XmlDocument package2)
        //{
        //    OperationResult results = new OperationResult();
        //    results = OperationResult.Empty;
        //    return results;
        //}

        //private static void MergeChangeXML(XmlDocument originalXML, XmlDocument additionalXML)
        //{
        //    #region Merge components
        //    XmlNodeList components = additionalXML.SelectNodes("//Component");
        //    foreach (XmlNode component in components)
        //    {
        //        XmlNode sourceComponent = originalXML.
        //            SelectSingleNode("//Component[@URL=\"" + component.Attributes["URL"].Value + "\"]");
        //        if (null == sourceComponent)
        //        {
        //            string rowsetDescriptor = string.Empty;
        //            if (null != component.Attributes["RowsetDescriptor"])
        //                rowsetDescriptor = component.Attributes["RowsetDescriptor"].Value;
        //            sourceComponent = MDDEManager.CreateComponentElement(originalXML, rowsetDescriptor, component.Attributes["URL"].Value);
        //        }
        //        XmlNode propertyCollection = sourceComponent.SelectSingleNode("Properties");
        //        if (propertyCollection == null)
        //        {
        //            propertyCollection = originalXML.CreateElement("Properties");
        //            sourceComponent.AppendChild(propertyCollection);
        //        }
        //        #region Merge Properties
        //        XmlNodeList properties = component.SelectNodes("Properties/Property");
        //        foreach (XmlNode property in properties)
        //        {
        //            XmlNode sourceProperty = sourceComponent.
        //                SelectSingleNode("Properties/Property[@Name=\"" + property.Attributes["Name"].Value + "\"]");
        //            if (null == sourceProperty)
        //            {
        //                sourceProperty = MDDEManager.CreatePropertyElement
        //                    (originalXML, property.Attributes["Name"].Value, property.Attributes["Value"].Value);
        //                propertyCollection.AppendChild(sourceProperty);
        //            }
        //            else
        //                sourceProperty.Attributes["Value"].Value = property.Attributes["Value"].Value;
        //        }
        //        #endregion

        //        #region Merge Mappings

        //        // Get the earlier mappings.
        //        XmlNode originalMappings =
        //            sourceComponent.SelectSingleNode("./Mappings");

        //        // Remove earlier mappings.
        //        if (originalMappings != null)
        //            sourceComponent.RemoveChild(originalMappings);

        //        // Get new mappings.
        //        XmlNode newMappings =
        //            component.SelectSingleNode("./Mappings");

        //        // Add new mappings.
        //        if (newMappings != null)
        //            sourceComponent.AppendChild(
        //                originalXML.ImportNode(newMappings, true));

        //        #endregion

        //        #region Merge Collection
        //        //ToDo: As update doesn't support collection properties change for now so I am not implimenting this
        //        #endregion
        //    }
        //    #endregion

        //    #region Merge connection manager
        //    XmlNodeList connections = additionalXML.SelectNodes("//ConnectionManager");
        //    foreach (XmlNode connection in connections)
        //    {
        //        XmlNode sourceConnection = originalXML.
        //            SelectSingleNode("//ConnectionManager[@Name=\"" + connection.Attributes["Name"].Value + "\"]");
        //        if (null == sourceConnection)
        //        {
        //            sourceConnection = MDDEManager.CreateConnectionElement(originalXML, connection.Attributes["Name"].Value);
        //        }
        //        XmlNodeList properties = connection.SelectNodes("Properties/Property");
        //        foreach (XmlNode property in properties)
        //        {
        //            XmlNode sourceProperty = sourceConnection.
        //                SelectSingleNode("Properties/Property[@Name=\"" + property.Attributes["Name"].Value + "\"]");
        //            if (null == sourceProperty)
        //            {
        //                sourceProperty = MDDEManager.CreatePropertyElement
        //                    (originalXML, property.Attributes["Name"].Value, property.Attributes["Value"].Value);
        //                sourceConnection.FirstChild.AppendChild(sourceProperty);
        //            }
        //            else
        //                sourceProperty.Attributes["Value"].Value = property.Attributes["Value"].Value;
        //        }
        //    }
        //    #endregion

        //    #region Merge variables
        //    XmlNodeList variables = additionalXML.SelectNodes("//Variable");
        //    foreach (XmlNode variable in variables)
        //    {
        //        XmlNode sourceVariable = originalXML.
        //            SelectSingleNode("//Variable[@URL=\"" + variable.Attributes["URL"].Value + "\"]");
        //        if (null == sourceVariable)
        //        {
        //            sourceVariable = MDDEManager.CreateVariableElement(originalXML, variable.Attributes["URL"].Value);
        //        }
        //        XmlNodeList properties = variable.SelectNodes("Properties/Property");
        //        foreach (XmlNode property in properties)
        //        {
        //            XmlNode sourceProperty = sourceVariable.
        //                SelectSingleNode("Properties/Property[@Name=\"" + property.Attributes["Name"].Value + "\"]");
        //            if (null == sourceProperty)
        //            {
        //                sourceProperty = MDDEManager.CreatePropertyElement
        //                    (originalXML, property.Attributes["Name"].Value, property.Attributes["Value"].Value);
        //                sourceVariable.FirstChild.AppendChild(sourceProperty);
        //            }
        //            else
        //                sourceProperty.Attributes["Value"].Value = property.Attributes["Value"].Value;
        //        }
        //    }
        //    #endregion
        //}

        //public static XmlElement CreateComponentElement(XmlDocument changeXML, string rowsetDescriptor, string uri)
        //{
        //    XmlElement component = changeXML.CreateElement("Component");

        //    XmlAttribute attribute = changeXML.CreateAttribute("ID");
        //    attribute.Value = "0";
        //    component.Attributes.Append(attribute);

        //    attribute = changeXML.CreateAttribute("RowsetDescriptor");
        //    attribute.Value = rowsetDescriptor;
        //    component.Attributes.Append(attribute);

        //    attribute = changeXML.CreateAttribute("URL");
        //    attribute.Value = uri;
        //    component.Attributes.Append(attribute);

        //    XmlNode properties = changeXML.CreateElement("Properties");

        //    component.AppendChild(properties);

        //    changeXML.FirstChild.AppendChild(component);

        //    return component;
        //}

        //public static XmlElement CreateConnectionElement(XmlDocument changeXML, string name)
        //{
        //    XmlElement connection = changeXML.CreateElement("ConnectionManager");

        //    XmlAttribute attribute = changeXML.CreateAttribute("Name");
        //    attribute.Value = name;
        //    connection.Attributes.Append(attribute);

        //    XmlNode properties = changeXML.CreateElement("Properties");

        //    connection.AppendChild(properties);

        //    changeXML.FirstChild.AppendChild(connection);

        //    return connection;
        //}

        //public static XmlElement CreateVariableElement(XmlDocument changeXML, string uri)
        //{
        //    XmlElement variable = changeXML.CreateElement("Variable");

        //    XmlAttribute attribute = changeXML.CreateAttribute("URL");
        //    attribute.Value = uri;
        //    variable.Attributes.Append(attribute);

        //    XmlNode properties = changeXML.CreateElement("Properties");

        //    variable.AppendChild(properties);

        //    changeXML.FirstChild.AppendChild(variable);

        //    return variable;
        //}

        //public static XmlElement CreatePropertyElement(XmlDocument changeXML, string name, string value)
        //{
        //    XmlElement property = changeXML.CreateElement("Property");

        //    XmlAttribute attribute = changeXML.CreateAttribute("Name");
        //    attribute.Value = name;
        //    property.Attributes.Append(attribute);

        //    attribute = changeXML.CreateAttribute("Value");
        //    attribute.Value = value;
        //    property.Attributes.Append(attribute);

        //    return property;

        //}
    }
}
